package ring_buffer

import (
	"context"
	"errors"
	"sync"
)

var (
	ErrQueueIsEmpty = errors.New("ringBuffer: queue is empty")
)

type BlockRingBuffer struct {
	queue    []interface{}
	writeIdx int
	readIdx  int
	notFull  chan struct{}
	size     int
	mu       sync.Mutex
}

func (b *BlockRingBuffer) IsFull() bool {
	b.mu.Lock()
	defer b.mu.Unlock()
	return b.writeIdx == b.readIdx+b.size
}

func (b *BlockRingBuffer) isFull() bool {
	return b.writeIdx == b.readIdx+b.size
}

func (b *BlockRingBuffer) IsEmpty() bool {
	b.mu.Lock()
	defer b.mu.Unlock()
	return b.writeIdx == b.readIdx
}

func (b *BlockRingBuffer) isEmpty() bool {
	return b.writeIdx == b.readIdx
}

func (b *BlockRingBuffer) Read() (interface{}, error) {
	b.mu.Lock()
	defer func() {
		b.queue[b.readIdx%b.size] = nil // 重置，断开引用，保证GC可以回收掉该位置的数据
		b.readIdx++
		b.mu.Unlock()
		select {
		case b.notFull <- struct{}{}:
		default:
		}
	}()
	if b.isEmpty() { // 不能用len(b.queue) 因为是环形 数组重复利用
		return nil, ErrQueueIsEmpty
	}
	return b.queue[b.readIdx%b.size], nil
}

func (b *BlockRingBuffer) Write(ctx context.Context, v interface{}) error {
	// 如果队列满了就阻塞生产者直至超时
	select {
	case _, ok := <-b.notFull: // 阻塞写入
		if !ok {
			return errors.New("队列已关闭")
		}
		b.mu.Lock()
		b.queue[b.writeIdx%b.size] = v
		b.writeIdx++
		b.mu.Unlock()
	case <-ctx.Done():
		return ctx.Err()
	}
	return nil
}

func NewBlockRingBuffer(size int) Ring {
	b := &BlockRingBuffer{
		queue:    make([]interface{}, size),
		writeIdx: 0,
		readIdx:  0,
		notFull:  make(chan struct{}, size),
		size:     size,
	}

	for i := 0; i < size; i++ {
		b.notFull <- struct{}{}
	}
	return b
}

var _ Ring = (*BlockRingBuffer)(nil)
var _ RingBufferCreator = NewBlockRingBuffer
